/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/11/03
*/
#include "Edit.h"
#include "../edui.h"
#include "../external/pcre++/pcre++.h"

EDUI_NAMESPACE_BEGIN

Edit* Edit::GetInterface()
{
    Edit* edit = new Edit;
#ifdef EDUI_TEST_NEW_DELETE
    ++m_newdeleteNum;
    Log("[Edit] new edit : %d", edit);
#endif
#ifdef EDUI_USE_LUAPLUS
    LuaMgrPtr->Add("Edit", edit);
#endif
    return edit;
}

void Edit::Release()
{
    if (m_timer){
        this->GetWindow()->RemoveTimer(m_timer);
        m_timer = 0;
    }
#ifdef EDUI_TEST_NEW_DELETE
    --m_newdeleteNum;
    Log("[Edit] delete edit : %s, %d", this->GetName(), this);
#endif
#ifdef EDUI_USE_LUAPLUS
    LuaMgrPtr->Remove(this);
#endif
    delete this;
}

Edit* Edit::ToEdit(Control* ctrl)
{
    return dynamic_cast<Edit*>(ctrl);
}

Edit::Edit() : m_iSelBegin(-1), m_iSelEnd(-1), m_iCursorPos(0), m_iCursorMaxPos(0), 
            m_bSeled(false), m_bSelEnabled(true), m_bCursorShow(false), m_bCursorEnabled(true), m_bCursorEditing(false), m_bReadOnly(false),
            m_iShiftSelDirection(0), m_textTextureOffset(0),
            /*m_textTexture(0),*/
            m_timer(0),
            m_maxChar(0), m_tipFont(0), m_bPassword(false),
            m_regexInput(0), m_regexVerify(0)
{
    m_state = BUTTON_STATE_NORMAL;
    m_textAlign = ALIGN_LEFT;
    m_bKeyboardEnabled = true;
    m_tipTextColor.Reset(127, 127, 127, 255);
    m_bDragWnd = false;
    m_passWordMask = "*";
    
    m_selBkColor.Reset(177, 177, 177, 255);
}

Edit::~Edit()
{
    if (m_regexInput){
        delete m_regexInput;
        m_regexInput = 0;
    }

    if (m_regexVerify){
        delete m_regexVerify;
        m_regexVerify = 0;
    }
}

void Edit::SetAttribute(const char* name, const char* value)
{
    if (0 == strcmp(name, "maxchar")){
        int i = 0;
        ParserInt(value, &i, 0);
        this->SetMaxChar(i);
    } else if (0 == strcmp(name, "tiptext")){
        this->SetTipText(value);
    } else if (0 == strcmp(name, "tipfont")){
        FontMgr* fontMgr = IEnginePtr->GetFontMgr();
        IFont* font = fontMgr->Get(value);
        if (font) this->SetTipFont(font);
    } else if (0 == strcmp(name, "tipfontid")){
        int id = -1;
        if (ParserInt(value, &id, -1) && (id >= 0)){
            FontMgr* fontMgr = IEnginePtr->GetFontMgr();
            IFont* font = fontMgr->Get(id);
            if (font) this->SetTipFont(font);
        }
    } else if (0 == strcmp(name, "password")){
        bool b = ParserBool(value, false);
        this->SetUsePassWord(b);
    } else if (0 == strcmp(name, "readonly")){
        bool b = ParserBool(value, false);
        this->SetReadOnly(b);
    } else if (0 == strcmp(name, "passwordmask")){
        this->SetPassWordMask(value);
    } else if (0 == strcmp(name, "regex")){
        this->SetInputRegex(value);
    } else if (0 == strcmp(name, "verifyregex")){
        this->SetVerifyRegex(value);
    } else {
        Label::SetAttribute(name, value);
    }
}

Color* Edit::GetDestColor(const char* value)
{
    if (0 == strcmp(value, "selbk")){
        return &m_selBkColor;
    } else if (0 == strcmp(value, "tiptext")){
        return &m_tipTextColor;
    }
    return Label::GetDestColor(value);
}

ImageData* Edit::GetDestImageData(const char* value)
{
    if (0 == strcmp(value, "normal")){
        return &m_normalImage;
    } else if (0 == strcmp(value, "hot")){
        return &m_hotImage;
    } else if (0 == strcmp(value, "pressed")){
        return &m_pressedImage;
    } else if (0 == strcmp(value, "disabled")){
        return &m_disabledImage;
    }
    return Label::GetDestImageData(value);
}

bool Edit::HandleMsgButton(Msg* msg)
{
    if (msg->msg == MSG_WND_LEAVE){
        if (m_state != BUTTON_STATE_NORMAL){
            m_state = BUTTON_STATE_NORMAL;
            this->Invaldate();
        }
    }

    if (msg->msg == MSG_MOUSE_MOVE){
        MouseMoveMsg *evt = &msg->motion;
        if (m_rc.IsInRect(evt->x, evt->y)){
            if (evt->state & MOUSE_BUTTON(MOUSE_BUTTON_LEFT)){
                if (m_state !=  BUTTON_STATE_PRESSED){
                    m_state =  BUTTON_STATE_PRESSED;
                    this->Invaldate();
                }
            } else {
                if (m_state !=  BUTTON_STATE_HOT){
                    m_state =  BUTTON_STATE_HOT;
                    this->Invaldate();
                }
            }
            return true;
        } else {
            if (m_state !=  BUTTON_STATE_NORMAL){
                m_state =  BUTTON_STATE_NORMAL;
                this->Invaldate();
            }
        }
    } else if (msg->msg == MSG_MOUSE_BUTTON_DOWN){
        MouseButtonMsg *evt = &msg->button;
        if (m_rc.IsInRect(evt->x, evt->y)){
            if (m_state !=  BUTTON_STATE_PRESSED){
                m_state =  BUTTON_STATE_PRESSED;
                this->Invaldate();
            }
            return true;
        }
    } else if (msg->msg == MSG_MOUSE_BUTTON_UP){
        MouseButtonMsg *evt = &msg->button;
        if (m_rc.IsInRect(evt->x, evt->y)){
            if (m_state !=  BUTTON_STATE_HOT){
                m_state =  BUTTON_STATE_HOT;
                this->Invaldate();
            }
            return true;
        } else {
            if (m_state !=  BUTTON_STATE_NORMAL){
                m_state =  BUTTON_STATE_NORMAL;
                this->Invaldate();
            }
        }
    }
    return false;
}

bool Edit::HandleMsgMouse(Msg* msg)
{
    if (msg->msg == MSG_MOUSE_MOVE){
        MouseMoveMsg *evt = &msg->motion;
        if (m_rc.IsInRect(evt->x, evt->y)){
            //left button pressed
            if (MOUSE_BUTTON_LMASK & evt->state){
                int pos = Mouse2CursorPos(evt->x, evt->y);
                if (pos != -1){
                    m_iSelEnd = pos;
                    if (m_iSelEnd != m_iSelBegin) m_bSeled = true;
                    this->Invaldate();
                }
            }
        }
    } else if (msg->msg == MSG_MOUSE_BUTTON_DOWN){
        MouseButtonMsg *evt = &msg->button;
        if (m_rc.IsInRect(evt->x, evt->y)){
            if (MOUSE_BUTTON_LEFT == evt->button){
                int pos = Mouse2CursorPos(evt->x, evt->y);
                if (pos != -1){
                    m_iCursorPos = pos;
                    m_iSelBegin = m_iCursorPos;
                    m_bSeled = false;
                   
                    Rect rc;
                    if (this->GetCursorRect(&rc)) this->GetWindow()->SetTextInputRect(&rc);
                   
                    this->Invaldate();
                }
            } else if (MOUSE_BUTTON_RIGHT == evt->button){
                LogDebug("Show Menu.");
            } else if (MOUSE_BUTTON_MIDDLE == evt->button){
                LogDebug("Copy");
                this->Copy();
            }
        }
    }
    return false;
}

bool Edit::HandleMsgKeyboard(Msg* msg)
{
    if (msg->msg == MSG_KEY_DOWN){
        KeyboardMsg* evt = &msg->key;
        if (KEYMOD_CTRL & evt->mod){
            if (evt->key == KEY_C){
                m_bCursorEditing = true;
                this->Copy();
            } else if (evt->key == KEY_X){
                m_bCursorEditing = true;
                this->Cut();
            } else if (evt->key == KEY_V){
                m_bCursorEditing = true;
                this->Paste();
            } else if (evt->key == KEY_A){
                m_bCursorEditing = true;
                this->SelAllText();
            }
        }
        if (evt->key == KEY_RETURN){
            this->DoEvent(EVENT_KEY_ENTER);
        } else if (evt->key == KEY_DELETE){
            m_bCursorEditing = true;
            this->Delete();
        } else if (evt->key == KEY_BACKSPACE){
            m_bCursorEditing = true;
            this->BackSpace();
        } else if (evt->key == KEY_LEFT){
            m_bCursorEditing = true;
            if (KEYMOD_SHIFT & evt->mod){
                if (m_iShiftSelDirection == 0) m_iShiftSelDirection = 1;

                int b, e;
                if (this->GetSel(&b, &e)){
                    if (m_iShiftSelDirection == 1) --b;
                    else --e;
                } else {
                    b = m_iCursorPos - 1;
                    e = b + 1;
                    m_iShiftSelDirection = 1;
                }
                this->SelText(b, e);
            } else this->SetCursorPrevPos();
        } else if (evt->key == KEY_RIGHT){
            m_bCursorEditing = true;
            if (KEYMOD_SHIFT & evt->mod){
                if (m_iShiftSelDirection == 0) m_iShiftSelDirection = 2;

                int b, e;
                if (this->GetSel(&b, &e)){
                    if (m_iShiftSelDirection == 2) ++e;
                    else ++b;
                } else {
                    b = m_iCursorPos;
                    e = b + 1;
                    m_iShiftSelDirection = 2;
                }
                this->SelText(b, e);
            } else this->SetCursorNextPos();
        }
    }

    if (msg->msg == MSG_TEXT_INPUT){
        m_bCursorEditing = true;
        TextMsg* evt = &msg->text;
        this->ReplaceSel(evt->text, false);
    }

    return true;
}

bool Edit::HandleMsg(Msg* msg)
{
    HandleMsgButton(msg);

    HandleMsgMouse(msg);
    HandleMsgKeyboard(msg);

    return Control::HandleMsg(msg);
}

void Edit::RenderStatusImages(IRender* render, const Rect* rc)
{
    if (!m_bEnabled){
        m_state = BUTTON_STATE_DISABLED;
    }

    if (m_state == BUTTON_STATE_NORMAL){
        this->DrawImage(render, &m_normalImage);
    } else if (m_state == BUTTON_STATE_HOT){
        this->DrawImage(render, &m_hotImage);
    } else if (m_state == BUTTON_STATE_PRESSED){
        this->DrawImage(render, &m_pressedImage);
    } else if (m_state == BUTTON_STATE_DISABLED){
        this->DrawImage(render, &m_disabledImage);
    }
}

void Edit::RenderText(IRender* render, const Rect* rc)
{
    String renderText;
    Color color = m_textColor;
    //get font
    IFont* font = this->GetFont();
    //get text
    const char* text = this->GetTranslateText();
    if (text && strlen(text) > 0){
        renderText = text;
    } else {
        renderText = m_tipText;
        color.Reset(&m_tipTextColor);
        font = this->GetTipFont();
    }

    if (font && !renderText.IsEmpty()){

        RenderSelBk(render);

        Size sz = font->GetSizeUTF8(renderText.GetString());    
        Rect rc = this->GetTextAlignRect(&sz);
        rc.x -= m_textTextureOffset;
        rc.w += m_textTextureOffset;
        render->DrawText(renderText.GetString(), &rc, font, &color);
    }

    RenderCursor(render);
}

void Edit::RenderSelBk(IRender* render)
{
    if (!m_bSelEnabled || !m_bSeled) return;

    Rect rc;
    if (!this->GetSelRect(&rc)) return;
    
    rc.x -= m_textTextureOffset;
    
    render->SetColor(&m_selBkColor);
    render->FillRect(&rc);
}

void Edit::RenderCursor(IRender* render)
{
    if (!m_bCursorEnabled || !m_bCursorShow || m_bSeled) return;

    Rect rc;
    if (!this->GetCursorRect(&rc)) return;

    rc.x -= m_textTextureOffset;

    render->SetColor(0, 0, 0, 255);
    render->FillRect(&rc);

    if (m_bCursorEditing){
        this->GetWindow()->SetTextInputRect(&rc);
    }
}

void Edit::UpdateTexture()
{
    m_iCursorMaxPos = UTF8_CharNumber(m_text.GetString());
    if (m_iCursorPos < 0) m_iCursorPos = 0;
    if (m_iCursorPos > m_iCursorMaxPos) m_iCursorPos = m_iCursorMaxPos;
    if (m_iSelBegin < 0) m_iSelBegin = 0;
    if (m_iSelEnd > m_iCursorMaxPos) m_iSelEnd = m_iCursorMaxPos;
    if (m_iSelBegin == m_iSelEnd) {
        m_bSeled = false;
    }

    // get m_textTextureOffset;
    Rect rc;
    this->GetCursorRect(&rc);

    //update texture
    //if (m_textTexture) m_textTexture->Destroy();

    this->Invaldate();
}

void Edit::UpdateCursor()
{
    if (!m_bCursorEnabled) return;
    if (m_bCursorEditing && m_bCursorShow){
        m_bCursorEditing = false;
        return;
    }
    m_bCursorShow = ! m_bCursorShow;
    this->Invaldate();
}

IFont* Edit::GetTipFont(bool bWindow) const
{
    if (!bWindow) return m_tipFont;
    if (m_tipFont) return m_tipFont;
    return this->GetWindow()->GetFont();
}

bool Edit::SetTipFont(IFont* font)
{
    if (!font) return false;
    m_tipFont = font;
    return true;
}

//-----------------------------------------//
// text changed.
void Edit::SetText(const char* text)
{
    if (!IsRegexMatch(m_regexInput, text)) return;

    int icheck = this->CheckMaxChar(text);
    if (0 > icheck) return;
    else if (0 == icheck){
        if (m_bPassword){
            m_passWordText = text;
            String tmp = this->GetSomeMaskChar(UTF8_CharNumber(text));
            m_text = tmp;
        } else m_text = text;
    } else {
        String str = text;
        str = str.Left(icheck);
        if (m_bPassword){
            m_passWordText += str;
            String tmp = this->GetSomeMaskChar(UTF8_CharNumber(str));
            m_text += tmp;
        } else m_text += str;
    }

    m_bSeled = false;
    m_iCursorPos = 0;

    this->UpdateTexture();

    this->DoEvent(EVENT_TEXTCHANGE);
}

bool Edit::ReplaceText(const char* text, int begin, int end, bool paste)
{
    if (m_bReadOnly) return false;
    if (!IsRegexMatch(m_regexInput, text)) return false;
    
    if (begin > end){
        int tmp = begin;
        begin = end;
        end = tmp;
    }
    if (begin < 0) begin = 0;
    if (end > m_iCursorMaxPos) end = m_iCursorMaxPos;

    Uint32 charnum = UTF8_CharNumber(text);

    int b, e, pb, pe;
    if (!this->TextIndex2Pos(m_text.GetString(), begin, end, &b, &e)) return false;
    if (m_bPassword && !this->TextIndex2Pos(m_passWordText.GetString(), begin, end, &pb, &pe)) return false;

    int icheck = this->CheckMaxChar(text, end - begin);
    if (0 > icheck) return false;
    else if (0 == icheck){
        if (m_bPassword){
            m_passWordText.Replace(text, pb, pe - pb);
            String tmp = this->GetSomeMaskChar(charnum);
            m_text.Replace(tmp.GetString(), b, e - b);
        }
        else m_text.Replace(text, b, e - b);
    } else {
        String str = text;
        str = str.Left(icheck);
        charnum = UTF8_CharNumber(str.GetString());
        if (m_bPassword){
            m_passWordText.Replace(str.GetString(), pb, pe - pb);
            String tmp = this->GetSomeMaskChar(charnum);
            m_text.Replace(tmp.GetString(), b, e - b);
        } else m_text.Replace(str.GetString(), b, e - b);
    }

    m_iSelBegin = begin;
    m_iSelEnd = begin + charnum;
    m_iCursorPos = m_iSelEnd;

    if (!paste) m_bSeled = false;

    this->UpdateTexture();

    this->DoEvent(EVENT_TEXTCHANGE);

    return true;
}

bool Edit::DeleteText(int begin, int end)
{
    if (m_bReadOnly) return false;
    if (begin > end){
        int tmp = begin;
        begin = end;
        end = tmp;
    }

    if (begin < 0) begin = 0;
    if (end > m_iCursorMaxPos) end = m_iCursorMaxPos;

    int b, e;
    if (!this->TextIndex2Pos(m_text.GetString(), begin, end, &b, &e)) return false;
    if (m_bPassword){
        int pb, pe;
        if (!this->TextIndex2Pos(m_passWordText.GetString(), begin, end, &pb, &pe)) return false;
        m_passWordText.Remove(pb, pe - pb);
    }
    m_text.Remove(b, e - b);

    m_bSeled = false;
    m_iCursorPos = begin;

    this->UpdateTexture();

    this->DoEvent(EVENT_TEXTCHANGE);

    return true;
}

void Edit::ClearText()
{
    m_text.Empty();
    m_passWordText.Empty();

    m_bSeled = false;
    m_iCursorPos = 0;

    this->DoEvent(EVENT_TEXTCHANGE);
    
    this->UpdateTexture();
}

//-----------------------------------------//
const char* Edit::GetText() const
{
    return m_text.GetString();
}

String Edit::GetText(int begin, int end) const
{
    if (begin > end){
        int tmp = begin;
        begin = end;
        end = tmp;
    }

    if (begin < 0) begin = 0;
    if (end > m_iCursorMaxPos) end = m_iCursorMaxPos;

    int b, e;
    if (!this->TextIndex2Pos(m_text.GetString(), begin, end, &b, &e)) return String();

    return m_text.Mid(b, e - b);
}

void Edit::SelAllText()
{
    this->SelText(0, m_iCursorMaxPos);
}

void Edit::SelText(int begin, int end)
{
    if (begin > end){
        int tmp = begin;
        begin = end;
        end = tmp;
    }

    if (begin < 0) begin = 0;
    if (end > m_iCursorMaxPos) end = m_iCursorMaxPos;
    m_iSelBegin = begin;
    m_iSelEnd = end;

    if (m_iSelBegin != m_iSelEnd) m_bSeled = true;
    else m_bSeled = false;

    this->Invaldate();
}

void Edit::ReplaceSel(const char* text, bool paste)
{
    if (m_bReadOnly) return;
    if (!text) return;

    if (m_bSeled){
        int b, e;
        if (this->GetSel(&b, &e)){
            this->ReplaceText(text, b, e, paste);
        }
    } else {
        this->ReplaceText(text, m_iCursorPos, m_iCursorPos, paste);
    }
}

void Edit::DeleteSel()
{
    if (m_bReadOnly) return;
    int b, e;
    if (!this->GetSel(&b, &e)) return;

    m_bSeled = false;
    m_iCursorPos = b;

    this->DeleteText(b, e);
}

bool Edit::GetSel(int* begin, int* end) const
{
    if (!m_bSelEnabled || !m_bSeled) return false;
    if (m_iSelEnd > m_iSelBegin){
        *begin = m_iSelBegin;
        *end = m_iSelEnd;
    } else {
        *begin = m_iSelEnd;
        *end = m_iSelBegin;
    }
    return true;
}

bool Edit::GetSelPos(int* begin, int* end) const
{
    int b, e;
    if (!this->GetSel(&b, &e)) return false;
    if (b < 0 || e <= 0) return false;

    Uint32 posb = 0, pose = 0, lenb = 0, lene = 0;
    if (!UTF8_QueryChar(m_text.GetString(), b + 1, &posb, &lenb)) return false;
    if (!UTF8_QueryChar(m_text.GetString(), e, &pose, &lene)) return false;

    *begin = posb;
    *end = pose + lene;

    return true;
}

bool Edit::TextIndex2Pos(const char* text, int inBegin, int inEnd,int* outBegin, int* outEnd) const
{
    if (!text) return false;
    if (inBegin < 0 || inEnd < 0) return false;

    if (inEnd == 0){
        *outBegin = 0;
        *outEnd = 0;
        return true;
    }

    if (inBegin == m_iCursorMaxPos){
        *outBegin = (int)strlen(text);
        *outEnd = *outBegin;
        return true;
    }

    Uint32 posb = 0, pose = 0, lenb = 0, lene = 0;
    if (!UTF8_QueryChar(text, inBegin + 1, &posb, &lenb)) return false;
    if (!UTF8_QueryChar(text, inEnd, &pose, &lene)) return false;

    *outBegin = posb;
    *outEnd = pose + lene;

    return true;
}

String Edit::GetSelText() const
{
    int b, e;
    if (!this->GetSel(&b, &e)) return String();
    return this->GetText(b, e);
}

void Edit::DeletePrev()
{
    if (m_bReadOnly) return;
    if (m_iCursorPos == 0) return;
    --m_iCursorPos;

    this->DeleteText(m_iCursorPos, m_iCursorPos + 1);
}

void Edit::DeleteNext()
{
    if (m_bReadOnly) return;
    if (m_iCursorPos == m_iCursorMaxPos) return;

    this->DeleteText(m_iCursorPos, m_iCursorPos + 1);
}

//copy,cut to clipboard.
//copy sel text.
void Edit::Copy()
{
    String text = this->GetSelText();
    if (text.IsEmpty()) return;
    IEnginePtr->SetClipboardText(text.GetString());
}

//cut sel text.
void Edit::Cut()
{
    String text = this->GetSelText();
    if (text.IsEmpty()) return;
    IEnginePtr->SetClipboardText(text.GetString());
    this->DeleteSel();
}

//paste text instead of sel text.
void Edit::Paste()
{
    char* text = IEnginePtr->GetClipboardText();
    if (!text) return;
    this->ReplaceSel(text);
}

//delete sel text
void Edit::Delete()
{
    if (m_bSeled) this->DeleteSel();
    else this->DeleteNext();
}

void Edit::BackSpace()
{
    if (m_bSeled) this->DeleteSel();
    else this->DeletePrev();
}

//about cursor
void Edit::SetCursorNextPos()
{
    if (m_bSeled){
        int b, e;
        if (this->GetSel(&b,&e)) this->SetCursorPos(e);
    } else {
        this->SetCursorPos(m_iCursorPos + 1);
    }
}

void Edit::SetCursorPrevPos()
{
    if (m_bSeled){
        int b, e;
        if (this->GetSel(&b,&e)) this->SetCursorPos(b);
    } else {
        this->SetCursorPos(m_iCursorPos - 1);
    }
}

void Edit::SetCursorPos(int pos)
{
    m_iCursorPos = pos;
    if (m_iCursorPos < 0) m_iCursorPos = 0;
    if (m_iCursorPos > m_iCursorMaxPos) m_iCursorPos = m_iCursorMaxPos;

    m_bSeled = false;

    this->Invaldate();
}

int Edit::GetCursorPos() const
{
    return m_iCursorPos;
}


void Edit::EnableCursor(bool bEnable)
{
    m_bCursorEnabled = bEnable;
}

void Edit::EnableSel(bool bEnable)
{
    m_bSelEnabled = bEnable;
}

void Edit::ReadOnly(bool bOnlyRead)
{
    m_bReadOnly = bOnlyRead;
}

// 0 if not limit.
void Edit::SetMaxChar(int maxchar)
{
    m_maxChar = maxchar;
}

void Edit::SetTipText(const char* text)
{
    if (!text) return;
    m_tipText = text;
}

const char* Edit::GetTipText() const
{
    return m_tipText.GetString();
}

void Edit::SetReadOnly(bool readonly)
{
    m_bReadOnly = readonly;
}

bool Edit::IsReadOnly() const
{
    return m_bReadOnly;
}

void Edit::SetUsePassWord(bool use)
{
    if (m_bPassword == use) return;
    m_bPassword = use;
    //update text
    if (use) this->SetText(m_text.GetString());
}

bool Edit::IsUsedPassWord() const
{
    return m_bPassword;
}

const char* Edit::GetPassWordText() const
{
    return m_passWordText.GetString();
}

void Edit::SetPassWordMask(const char* mask)
{
    if (!mask) return;
    Uint32 pos = 0, len = 0;
    if (!UTF8_QueryChar(mask, 1, &pos, &len)) return;
    m_passWordMask = mask;
    m_passWordMask = m_passWordMask.Left(len);
    //update text
    if (m_bPassword) this->SetText(m_passWordText.GetString());
    else this->SetText(m_text.GetString());
}

const char* Edit::GetPassWordMask() const
{
    return m_passWordMask.GetString();
}

//set input limit by regex
void Edit::SetInputRegex(const char* regex)
{
    if (m_regexInput){
        delete m_regexInput;
        m_regexInput = 0;
    }
    if (!regex) return;
    m_regexInput = new pcrepp::Pcre(regex);
}

//set verify limit by regex
void Edit::SetVerifyRegex(const char* regex)
{
    if (m_regexVerify){
        delete m_regexVerify;
        m_regexVerify = 0;
    }
    if (!regex) return;
    m_regexVerify = new pcrepp::Pcre(regex);
}

// verify edit text if ok by verify regex
// if not set verify regex,return true.
bool Edit::Verify()
{
    const char* text = 0;
    if (this->IsUsedPassWord()) text = this->GetPassWordText();
    else text = this->GetText();
    return this->IsRegexMatch(m_regexVerify, text);
}

// verify edit text if ok by given regex
// if regex is 0,return false
bool Edit::Verify(const char* regex)
{
    if (!regex) return false;
    const char* text = 0;
    if (this->IsUsedPassWord()) text = this->GetPassWordText();
    else text = this->GetText();

    pcrepp::Pcre pcre(regex);
    return this->IsRegexMatch(&pcre, text);
}

// return true if pcre==0; return false if text==0;
bool Edit::IsRegexMatch(void* pcre, const char* text)
{
    if (!text) return false;
    if (!pcre) return true;

    ((pcrepp::Pcre*)pcre)->search(text);

    return ((pcrepp::Pcre*)pcre)->matched();
}

//check limit maxchar.return->0:ok,>0: pos if maxchar,<0 error.
int Edit::CheckMaxChar(const char* text, int ignoreSize) const
{
    if (m_maxChar <= 0) return 0;
    if (!text) return -1;

    Uint32 num = UTF8_CharNumber(text);

    int iret = num + m_iCursorMaxPos - ignoreSize - m_maxChar;
    if (iret <= 0) return 0;

    Uint32 pos = 0, len = 0;
    if (!UTF8_QueryChar(text, num - iret, &pos, &len)) return -1;

    return pos + len;
}

String Edit::GetSomeMaskChar(int num)
{
    String str;
    for (int i = 0; i < num; ++i){
        str += m_passWordMask;
    }
    return str;
}

int Edit::Mouse2CursorPos(int x, int y)
{
    if (m_text.IsEmpty()) return -1;

    //get font
    IFont* font = this->GetFont();
    if (!font) return -1;

    Size szall = font->GetSizeUTF8(m_text.GetString());
    Rect rc = this->GetTextAlignRect(&szall);

    if (x <= rc.x) return 0;

    Uint32 pos = 0, len = 0;
    for (int i = 1; i <= m_iCursorMaxPos; ++i){
        if (UTF8_QueryChar(m_text.GetString(), i, &pos, &len)){
            Size sz = font->GetSizeUTF8(m_text.Left(pos + len).GetString());
            if (x < rc.x + sz.w){
                return (i - 1);
            }
        }
    }

    return m_iCursorMaxPos;
}

bool Edit::GetSelRect(Rect* brc)
{
    if (m_text.IsEmpty()) return false;
    int b, e;
    if (!this->GetSelPos(&b, &e)) return false;

    //get font
    IFont* font = this->GetFont();
    if (!font) return false;
   
    Size szall = font->GetSizeUTF8(m_text.GetString());
    Rect rc = this->GetTextAlignRect(&szall);

    brc->x = rc.x;
    brc->w = 0;
    brc->y = rc.y + (rc.h - szall.h) / 2;
    brc->h = szall.h;

    m_textTextureOffset = 0;

    Size sz = font->GetSizeUTF8(m_text.Left(b).GetString());
    brc->x = rc.x + sz.w;
    sz = font->GetSizeUTF8(m_text.Mid(b, e - b).GetString());
    brc->w = sz.w;

    if (brc->x + brc->w > rc.x + rc.w) m_textTextureOffset = brc->x + brc->w - rc.x - rc.w;

    return true;
}

bool Edit::GetCursorRect(Rect* brc)
{
    if (m_iCursorPos < 0 || m_iCursorPos > m_iCursorMaxPos) return false;

    //get font
    IFont* font = this->GetFont();
    if (!font) return false;

    Size szall = font->GetSizeUTF8(m_text.GetString());
    Rect rc = this->GetTextAlignRect(&szall);

    brc->x = rc.x;
    brc->w = 1;
    brc->y = rc.y + (rc.h - szall.h) / 2;
    brc->h = szall.h;

    m_textTextureOffset = 0;

    if (m_iCursorMaxPos == 0) return true;

    Uint32 pos = 0, len = 0;
    if (UTF8_QueryChar(m_text.GetString(), m_iCursorPos, &pos, &len)){
        Size sz = font->GetSizeUTF8(m_text.Left(pos + len).GetString());

        brc->x = rc.x + sz.w;
    }

    if (brc->x + brc->w > rc.x + rc.w) m_textTextureOffset = brc->x + brc->w - rc.x - rc.w;

    return true;
}

Uint32 _TimerCallback(TimerID id, Uint32 interval, IWindow* wnd, void *data1, void* data2)
{
    Edit* pthis = (Edit*)data1;
    if (!pthis) return interval;

    pthis->UpdateCursor();

    return interval;
}

void Edit::OnFocus()
{
    if (!m_timer){
        m_timer = this->GetWindow()->AddTimer(500, _TimerCallback, this);
    }

    this->GetWindow()->SetInputFocus();

    this->GetWindow()->StartTextInput();
    //Log(&m_rc); if wnd not show,error.
    this->GetWindow()->SetTextInputRect(&m_rc);

    Label::OnFocus();
}

void Edit::OnKillFocus()
{
    if (m_timer){
        this->GetWindow()->RemoveTimer(m_timer);
        m_timer = 0;
    }

    this->GetWindow()->StopTextInput();

    if (m_bCursorShow){
        m_bCursorShow = false;
        this->Invaldate();
    }
    Label::OnKillFocus();
}

void Edit::DoEvent(EventType type, void* data1, void* data2)
{
    switch (type){
        case EVENT_TEXTCHANGE : this->evt.textChange.emit(this); break;
        case EVENT_KEY_ENTER :  this->evt.onEnter.emit(this); break;
    }
#ifdef EDUI_USE_LUAPLUS
    switch (type){
        case EVENT_TEXTCHANGE : LuaMgrPtr->DoEvent(this, "textChange"); break;
        case EVENT_KEY_ENTER :  LuaMgrPtr->DoEvent(this, "onEnter"); break;
    }
#endif
}

EDUI_NAMESPACE_END
